/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = conntrack.h                                        */
/*  NOTE      = conntrack相关结构体定义                            */
/*  DATE      = 2014/09/29                                         */
/*******************************************************************/

#ifndef __CONNTRACK_H_
#define __CONNTRACK_H_

#include <pthread.h>
#include <arpa/inet.h>
//#include <uuid.h>
//#include <asm/atomic.h>
#include <event2/event.h>
#include <event2/event_struct.h>

#include "frcomm.h"
#include "frlist.h"
#include "frstr.h"
#include "frsutl.h"
#include "frtimer.h"

#include "module_comm.h"

#define APM_SESSION_START    1  /* session开始 */
#define APM_SESSION_STOP     2  /* session结束 */

#define APM_L7PROTO_STATUS_UNKNOWN      0  /* L7层协议判断未知 */
#define APM_L7PROTO_STATUS_IN_PROGRESS  1  /* L7层协议判断进行中 */
#define APM_L7PROTO_STATUS_FAIL         2  /* L7层协议判断失败 */
#define APM_L7PROTO_STATUS_DONE         3  /* L7层协议判断完成 */

//#define APM_L7_IN_PROGRESS 0   /* 正在分析中 */
//#define APM_L7_REQ_DONE    1   /* request分析已经完成  */
//#define APM_L7_DONE        2   /* response分析已经完成  */

#define APM_CONNTRACK_SESSION_UNLOCKED  0  /* i_sesion_locked标志位为0 */
#define APM_CONNTRACK_SESSION_LOCKED    1

/* ct 连接状态 */
#define APM_CONNECT_SYN_SENT        1
#define APM_CONNECT_SYN_RECV        2
#define APM_CONNECT_ESTABLISHED     3
#define APM_CONNECT_CLOSED          4
#define APM_CONNECT_TIMEOUT         5

/* 模块识别状态 */
#define APM_MODULE_STATUS_UNKNOWN       0   /* 未知 */
#define APM_MODULE_STATUS_AUOTODETECT   1   /* 自动识别的模块 */
#define APM_MODULE_STATUS_DEFINE        2   /* 用户指定的模块 */

struct __st_conntrack;

/*
*  按L7层协议解码后数据
*/
//typedef enum Lua_datatype {
//    NUMBER = 1,
//    STRING
//} Lua_datatype;
//#define LUA_NUMBER  1
//#define LUA_

/* Lua解析后数据 */
//typedef struct __st_L7_luadata {
//    char* p_field_name; /* 字段名 */
//
//    enum Lua_datatype  datatype; /* 数据类型，支持两种 number和string */
//    void* p_field_data; /* 数据值 */
//    unsigned int ui_datalen; /* 数值大小 */
//} L7_luadata;

/*
*   记录用户登录信息，例如：
*    1. ORACLE用户登录后，应生成L7_SESSION数据，SID等相关信息记录在p_session_private指向的结构体中；
*    2. 对于HTTP协议，用户登录后应SESSION数据
*/
typedef struct __st_l7_session {
    char arr_session_id[APM_UUID_SIZE];  /* 内部使用的session id，用户向实时数据分析平台发送数据时用，jsession_id等应存放在session_private中 */

    /* session数据 */
    char *p_username; /* 用户登录的用户名 */
    struct timeval st_session_starttime;/* 用户登录时间 */
    struct timeval st_session_stoptime; /* 用户退出时间 */

    void *p_session_private; /* 除用户名外，不同应用个性化信息（例如ORACLE登录时的客户端名称、版本号、SID等）保存在这里 */
    //int (**pp_session_format)(void *p_session_private); /* session format函数，用于格式化session_private数据，从module定义中复制过来 */
    //int (**pp_session_destory)(void *p_session_private);/* session destory函数，用于释放session_private数据，从module定义中复制过来 */

    int i_status; /* 状态，APM_SESSION_START表示开始一个session， APM_SESSION_STOP表示该session已经终止 */
} L7_session;

/*
* 协议分析数据
*   考虑到协议嵌套的情况，我们把每种协议分析结果存入L7_data_list链表
*   当request请求分析完成后（所有嵌套协议完成后），status标志位应设置为APM_L7_REQ_DONE，并向实时数据平台发送数据
*   同理，response请求分析完成后，status应设置为APM_L7_DONE，并向实时数据平台发送数据
*
*   当response数据完成向实时数据平台的发送后，L7数据应被立即释放
*   当连接timeout后，L7数据也应被释放
*
*/

typedef struct __st_L7_analyzer {
    L7_module *p_st_l7_module;          /* 协议解析模块(没有协议号时NULL) todo释放该模块时,减少引用次数 */
    
    //int (**pp_l7_analyze)(struct __st_conntrack *p_st_conn, Analyz_data_in *p_st_data_in, Analyz_data_out *p_st_data_out); /* 用于L7层数据解码 */

    /* L7层分析结果 */
    L7_session      *p_st_session;      /* 当前L7的session */
    L7_transaction  *p_st_transaction;  /* 当前分析的交易 */

} L7_analyzer;

/*
*   关联链路分析用
*   例如，Oracle收到Redirect包后，根据包体内容获得重定向端口后，应首先创建conntrack_expecting
*      主进程当收到SYN包后:
*        1. 根据3要素查找当前列表，并将master赋值给自己的master，master对应的refcount++
*        2. 将此conntrack_expecting脱链
*/
typedef struct __st_exp
{
    unsigned int ui_exp_refcnt;           /* 引用次数(原子操作)，引用次数<=0时才能释放 */

    u_short us_proto;   /* 6(IPPROTO_TCP) / 17(IPPROTO_UDP) */
    u_int   ui_src;     /* 客户端ip */
    u_int   ui_dest;    /* 希望连接服务器的ip */
    u_short us_dport;   /* 希望连接服务器的port */

    struct __st_conntrack *p_st_master;  /* 一旦找到指定的连接，应将该连接设置为master的从连接 */

    Timer_entry *p_st_timer_entry;
} Frexp;

/*
* 连接跟踪
*/
typedef struct __st_conntrack {
    char arr_connection_id[APM_UUID_SIZE];  /* 内部使用的connection id，用户向实时数据分析平台发送数据时用 */
    pthread_spinlock_t lock; /* 自旋锁 */

    unsigned int refcnt;            /* 引用次数(原子操作)，引用次数<=0时才能释放 */
    struct __st_conntrack *master;  /* 关联链路父链路 */
    Frexp *p_st_exp_master;         /* 关联链路管理的节点(由于ct->src_ip有可能会变成dst_ip,故根据ip+port有可能查询不到关联连接) */

    uint16_t  flg_update:1,           /* 有数据包:FR_YES, 否则:FR_NO */
              flg_status:7,           /* 连接状态： APM_CONNECT_SYN_SENT/APM_CONNECT_SYN_RECV
                                                  APM_CONNECT_ESTABLISHED/APM_CONNECT_CLOSED/APM_CONNECT_TIMEOUT */
              flg_inTask:1,           /* FR_YES:增加到taskskb_mng,FR_NO:没有增加到taskskb_mng */
              flg_ct_direct:1,        /* FR_DIRECT_UNKNOWN FR_DIRECT_KNOWN */
              flg_report_dead_conn:1, /* 僵尸连接上报标识  FR_YES:已上报 FR_NO:未上报*/
              flg_report_session_timeout:1,/* session timeout标识  FR_YES:已上报 FR_NO:未上报*/
              flg_reserve:4;

    struct timeval st_connection_created;      /* 连接创建时间 */
    struct timeval st_connection_established;  /* 连接成功时间，TCP为三次握手后时间，UDP=connection_created */
    struct timeval st_connection_lastskb_time; /* 最后收发数据包时间 */
    struct timeval st_connection_close_time;   /* 数据包close时间(fin/reset) */

    /* Timer function; drops refcnt when it goes off. */
    /* UDP timer?? */
    Timer_entry *p_st_timer_entry;

    /* 基本信息
     * 这里假定客户端发起,服务端的信息保存到dest里。
     * 不考虑服务端主动连接的情况,这种情况时,把服务器的信息还是保存到dest里 */
    u_short ip_proto;             /* 6(IPPROTO_TCP) / 17(IPPROTO_UDP) */
    char src_mac[APM_MAC_SIZE];   /* src mac */
    char dst_mac[APM_MAC_SIZE];   /* dst mac */
    u_int src_ip;
    u_short src_port;
    u_int dst_ip;
    u_short dst_port;

    Frlist_header lst_cap_queue;    /* 数据包缓存队列,FIFO */
    unsigned int ui_done_data_cnt;  /* 已处理数据包个数 */
    int i_flag_queue_redo;          /* FR_YES:数据包重新插入到queue_hdr */

    Frlist_header lst_cap_peek;     /* 处理过程中缓存的数据 */
    int i_flag_cap_peek;            /* FR_YES:保留已经使用的数据;否则queue_peek里不保留该数据 */
    int i_flag_peek2queue;          /* FR_YES:lst_cap_peek数据转移到lst_cap_queue */

    //pthread_t  thread_id;         /* 当前正在处理的线程 */

    int i_flag_module_status;         /* APM_MODULE_STATUS_UNKNOWN/APM_MODULE_STATUS_AUOTODETECT/APM_MODULE_STATUS_DEFINE */
    unsigned int ui_define_ver;       /* 用户指定协议时有效,Frproto_mng->ui_ver */

    Frlist_header lst_hdr_l7proto;    /* 协议模块列表,按次序存放,存放协议号  内部节点为 L7_analyzer (获取时使用ct->lock) */
    int i_active_l7proto_idx;         /* 当前正在分析的协议(从0开始) */
    int i_flag_l7proto_status;        /* 协议分析状态: APM_L7PROTO_STATUS_UNKNOWN, APM_L7PROTO_STATUS_IN_PROGRESS
                                         , APM_L7PROTO_STATUS_FAIL, APM_L7PROTO_STATUS_DONE */

    L4_transaction st_l4_transaction;

    /* 包数据统计信息 */
    unsigned long ul_rx_packets;
    unsigned long ul_tx_packets;
    unsigned long ul_drop_packets;

    unsigned long ul_rx_bytes;
    unsigned long ul_tx_bytes;

} Conntrack;

/*
 *  用于记录某主机对外提供哪些服务
 */
typedef struct __st_host_service {
    int i_ip_proto;   /* 6(IPPROTO_TCP) / 17(IPPROTO_UDP) */
    u_int   ip;
    u_short port;
    Frlist_header lst_hdr_l7_proto;  /* 适用的协议,按次序存放,存放协议号 */
} Host_service;


#endif
